前述章節以敘述過Spring Component註解的原理與運用,Spring核心所提供的幾項內建模式註解元件類別大多擴展於Component元件,通過此元件也可自定義所需的模式註解,便於各位開發者定義各類註解模式,以便劃分各種服務類別,以達到近十年來的主要領域驅動設計(DDD,Domain-Driven Design),此設計模式也是為目前流行的為服務架構的理論基礎,眾多服務要如何達到此理念的核心目的?主要需劃分各類服務類型元件,將各種服務細分化,再依照各種群組進行整合,所以可看出所有元件的核心來源註解稱之元件(@Component),透過擴展個元件去分類各種元件類型為服務類型(@Service)、倉儲庫事務交易類型(@Repository)或控制器類型(@Controller)等等,並在整合成一個大型服務稱之為一套系統,故開發者細分化這些元件類行為相當重要。
Spring核心所提供的幾項註解是可提供合併整合運用,並採用樹狀連結擴展方式將相關Spring的模式元件進行註冊,其拓展目標為透過元註解(@Component)進行擴展搜尋註冊,首當其衝的是,開發者須注意多重實作問題,無論何種註解在遇到此情況,皆會發生找不到相關的元件注入衝突,此時必須配置Bean名稱或配置權限(@Primary),不然預設都會是以類別名稱配置做為暫存在IoC配置池中索引名稱,即會造成尋找不到您的多重實作類別,我們將以下方程式碼做詳敘介紹。
- 定義Spring內部提供之服務模式註解(@Service)
1.1 配置介面,以便進行繼承多重實作
public interface NoteBookSellerService {
List<NoteBook> getAllNoteBookList();
NoteBook getNoteBookById(String id);
List<NoteBook> updateNoteBookById(NoteBook noteBook);
List<NoteBook> createNoteBookById(NoteBook noteBook);
}
1.2 進行繼承該介面,並配置服務模式註解及優先權,通過測試後,可發現到無需配置相關候選名稱即可自動注入首要服務模式元件。
@Primary
@Service
public class NoteBookSellerServiceImpl implements NoteBookSellerService {
static List<NoteBook> noteBookList = new ArrayList<NoteBook>();
static {
noteBookList = new ArrayList<NoteBook>(Arrays.asList(
new NoteBook("1","A8460","ASUS","Release on future by Asus ! "),
new NoteBook("2","MGOLD8888","APPLE","Release on future by Apple ! "),
new NoteBook("3","HI9999","ACER","Release on future by Acer ! ")
));
}
@Override
public List<NoteBook> getAllNoteBookList() {
return noteBookList;
}
...
...
...
}
@Autowired
NoteBookSellerService noteBookSellerService;
@Test
public void testGetAllNoteBookList() {
...
List<NoteBook> noteBookList = noteBookSellerService.getAllNoteBookList();
assertEquals(noteBookList.size() ,3);
...
...
}
- 運用配置開發者所需的新模式註解,促使Spring 核心自動搜索到此關聯模式註解,並將此元件自動註冊入IoC配置池中,以便進行後續的服務注入行為。
2.1 我們這邊建構一個新服務類型稱之UniversalService,並以擴展原始服務模式註解(@Service)進行設計。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface UniversalService {
@AliasFor(annotation = Service.class)
String value() default "";
}
2.2 將此註解配置於新實作上,已達一種介面多種實作不同註解,並透過不同的BeanName進行注入,可看出最初的實作元件(NoteBookSellerServiceImpl)僅有定義服務註解(@Service)及優先權(Primary),如何達到不衝突,亦可自動注入,即在第二個實作配置候選元件名稱,或所有元件都配置名稱方式,即可達到需求。
@UniversalService("SecondHandSeller")
public class SecondHandSellerImpl implements NoteBookSellerService {
static List<NoteBook> noteBookList = new ArrayList<NoteBook>();
static {
noteBookList = new ArrayList<NoteBook>(Arrays.asList(
new NoteBook("1","ZenBook 14 UX425EA","ASUS","Release version on 2021 ! "),
new NoteBook("2","M1X MacBook Pro","APPLE","Release version on 2021 ! "),
new NoteBook("3","SF514-55TA","ACER","Release version on 2021 ! ")
));
}
@Override
public List<NoteBook> getAllNoteBookList() {
return noteBookList;
}
.....
.....
.....
}
2.3 透過測試進行判斷各種類型資料,即可確認我們已達到多種註解擴展分類與實作之目的。
public class NoteBookTestSuite extends ServiceTestBase {
@Qualifier("SecondHandSeller")
@Autowired
NoteBookSellerService secondSellerService;
@Test
public void testGetAllNoteBookListBySecondhand() {
List<NoteBook> noteBookList = secondSellerService.getAllNoteBookList();
.....
assertEquals(noteBookList.size() ,3);
.....
}
}
透過上方各項實作,開發者可再透過各種註解去分辨你的服務類型,以便達到更佳的專案管理效用。
先前我們已說元註解的架構,所有的模式註解皆擴展(extends)於元註解(@Component),開發者可自行設計註解進行擴展元註解(@Component)或相關模式註解(@Service),但在分類模式註解時,需注意@Target及@Retention兩項註解都需與擴展的元註解一致,故我們可從架構圖上得知,無論何種註解要進行擴展繼承,且要沿用元註解(@Service)的各項欄位,此時可透過@AliasFor(亦同類別super方法)傳遞參數,當系統啟動並觸發Spring核心內部啟動掃描註解(@Annotation)時,將以樹狀結構進行掃描後,在自動註冊於IoC配置池中,開發者亦可透過BeanFactory直接獲取所有已註冊之元件類別。
圖一 @Service註解架構圖
Run test task
gradle test
Run open result html
open ./build/reports/tests/test/index.html
Customer service annotation test
Mind-blowing test detail
Where Should the Spring @Service Annotation Be Kept?
4.12 Classpath scanning, managed components and writing configurations using Java
到今天我們已經許多服務概念元件都提出來了,相信各位開發者對註解基本設計風格已有見解,明天開始我們將提出微服務架構及效能改進原理設計,因為天數不多,所以編排較緊,再請各位開發者見諒。